import java.util.HashMap;
import java.util.Map.Entry;
import java.lang.StringBuilder;
import java.util.ArrayList;

/**
 * Inmplementation of associative arrays in the Graphr Language
 * @author Paul Dix
 */
public class GraphrAssociativeArray extends GraphrDataType implements GraphrCollection {
	//Variable declaration
	private HashMap<String, GraphrDataType> hashMap;
	
	/**
	 * Constructs an empty GraphrAssociativeArray
	 */
	public GraphrAssociativeArray() {
		this.hashMap = new HashMap<String, GraphrDataType>();
	}
	
	/**
	 * Returns the hash map of this GraphrAssociativeArray object.
	 * @returns the hash map of this GraphrAssociativeArray object.
	 */
	public HashMap<String, GraphrDataType> getHashMap() {
		return this.hashMap;
	}
	
	/**
	 * Associates the specified value with the specified key in this associative array.
	 * @param key - key with which the specified value is to be associated.
       * @param value - value to be associated with the specified key.
	 */
	public void put(String key, GraphrDataType value) {
		this.hashMap.put(key, value);
	}
	
	/**
	 * Returns a string representation of the object.
	 * @returns a string representation of the object.
	 */
	public String toString() {
		StringBuilder string = new StringBuilder();
		string.append("[ ");
		for (Entry entry : this.hashMap.entrySet()) {
			string.append(entry.getKey() + " => " + entry.getValue() + ", ");
		}
		return string.toString().substring(0, string.length() - 2) + "]";
	}
	
	/**
	 * Returns the GraphrDataType at the location provided by key.
	 * @param key -the key whose associated value is to be returned.
	 * @returns GraphrDataType at the location provided by key.
	 */
	public GraphrDataType getValueAt(GraphrDataType key) {
		return this.hashMap.get(((GraphrString)key).getString());
	}
	
	/**
	 * Returns the GraphrDataType at the location provided by key.
	 * @param key -the key whose associated value is to be returned.
	 * @returns GraphrDataType at the location provided by key.
	 */
	public GraphrDataType getValueAt(String key) {
		return this.hashMap.get(key);
	}
	
	/**
	 * Associates the specified value with the specified key in this associative array.
	 * @param key - key with which the specified value is to be associated.
       * @param value - value to be associated with the specified key.
	 */
	public void setValueAt(GraphrDataType key, GraphrDataType value) {
		this.hashMap.put(((GraphrString)key).getString(), value);
	}
	
	/**
	 * Returns <b>true</b> if this associative array contains a mapping for the specified key.
	 * @param key - the key whose presence in this associative array is to be tested.
	 * @returns <b>true</b> if this associative array contains a mapping for the specified key; <b>false</b> otherwise.
	 */
	public boolean contains(String key) {
		return this.hashMap.containsKey(key);
	}

	/**
	 * Returns <b>true</b> if this associative array equals rVal; <b>false</b> otherwise.
	 * @param rVal - the GraphrDataType to compare with.
	 * @returns <b>true</b> if this associative array equals rVal; <b>false</b> otherwise.
	 */	
	public GraphrBoolean equals(GraphrDataType rVal) {
	  if (rVal.getClass() == this.getClass()) {
	    return new GraphrBoolean(this.hashMap.equals(((GraphrAssociativeArray)rVal).getHashMap()));
	  }
	  else {
	    return new GraphrBoolean(false);
	  }
	}
	
	/**
	 * Returns array list of keys for this associative array.
	 * @returns array list of keys for this associative array.
	 */
	public ArrayList<GraphrDataType> getArray() {
	  ArrayList<GraphrDataType> keys = new ArrayList<GraphrDataType>();
	  for (String key : this.hashMap.keySet()) {
	    keys.add(new GraphrString(key));
	  }
	  return keys;
	}
}